import type { DomReadyOptions } from '../../../types/interfaces'
import { FunctionUtils } from './function'

/**
 * 异步工具类
 */
export class AsyncUtils {
  /**
   * 等待条件满足后执行
   * @param condition 条件函数，返回 true 时执行 handler
   * @param handler 要执行的处理函数
   */
  static waitForCondition(
    condition: () => boolean,
    handler: () => void,
  ): void {
    this.pollUntilCondition(handler, {
      shouldExecute: () => condition(),
    })
  }

  /**
   * 条件性延迟执行 - 延迟执行任务，满足条件后立即停止
   * @param action 要执行的动作函数
   * @param settings 执行设置
   * @param [settings.pollingInterval] - 轮询间隔时间（毫秒）
   * @param [settings.timeout] - 超时时间（毫秒），超过时间自动停止
   * @param [settings.shouldExecute] - 执行条件函数，返回 true 时才执行 action
   * @param [settings.exitCriteria] - 退出标准函数，返回 true 时退出轮询
   */
  static pollUntilCondition(
    action: () => void,
    settings?: {
      pollingInterval?: number
      timeout?: number
      shouldExecute?: () => boolean
      exitCriteria?: () => boolean
    },
  ): void {
    // 0 表示不超时
    const { pollingInterval = 100, timeout = 0, shouldExecute = () => true, exitCriteria = () => true } = settings || {}

    const startTime = Date.now()

    const pollerId = window.setInterval(() => {
      if (timeout > 0 && Date.now() - startTime > timeout) {
        window.clearInterval(pollerId)
        return
      }

      if (!shouldExecute())
        return

      action()

      if (exitCriteria())
        window.clearInterval(pollerId)
    }, pollingInterval)
  }

  /**
   * 创建一个延迟指定时间的Promise
   * @param ms 延迟时间(毫秒)
   * @param options 配置选项
   * @param options.signal 可选的 AbortSignal 用于取消延迟
   * @returns 在指定时间后 resolve 的Promise
   */
  static delay(ms: number, options: { signal?: AbortSignal } = {}): Promise<void> {
    if (ms === 0) {
      if (options.signal?.aborted) {
        return Promise.reject(new DOMException('Delay was aborted', 'AbortError'))
      }
      return Promise.resolve() // 立即 resolve
    }

    return new Promise((resolve, reject) => {
      let timeoutId: number | null = null

      // 前置声明
      function cleanup() {
        if (timeoutId !== null) {
          clearTimeout(timeoutId)
          timeoutId = null
        }
        options.signal?.removeEventListener('abort', onAbort)
      }

      function onAbort() {
        cleanup()
        reject(new DOMException('Delay was aborted', 'AbortError'))
      }

      if (options.signal?.aborted) {
        reject(new DOMException('Delay was aborted', 'AbortError'))
        return
      }

      timeoutId = window.setTimeout(() => {
        cleanup()
        resolve()
      }, ms)

      options.signal?.addEventListener('abort', onAbort)
    })
  }

  /**
   * DOM 加载处理器
   * @param callback 回调函数
   * @param options 配置选项
   * @returns 取消函数
   */
  static whenDomReady(
    callback: () => void,
    options: DomReadyOptions = {},
  ): () => void {
    const {
      phase = 'document-idle',
      runImmediately = true,
      timeout,
      onTimeout,
    } = options

    let timer: number | undefined
    let isCancelled = false
    let eventListener: (() => void) | null = null

    const cleanup = () => {
      if (timer) {
        clearTimeout(timer)
        timer = undefined
      }
      if (eventListener) {
        document.removeEventListener('DOMContentLoaded', eventListener)
        eventListener = null
      }
    }

    const execute = () => {
      if (isCancelled)
        return

      cleanup()
      callback()
    }

    const handlePhase = () => {
      switch (phase) {
        case 'document-start':
          // 尽早执行，但需要确保基本DOM API可用
          if (typeof document.addEventListener === 'function') {
            document.addEventListener('DOMContentLoaded', execute)
          }
          else {
            setTimeout(() => handlePhase(), 50)
          }
          break

        case 'document-body':
          if (document.body) {
            execute()
          }
          else {
            const observer = new MutationObserver(() => {
              if (document.body) {
                observer.disconnect()
                execute()
              }
            })
            observer.observe(document.documentElement, { childList: true })
          }
          break

        case 'document-end':
        case 'document-idle':
          if (document.readyState !== 'loading') {
            if (runImmediately) {
              execute()
            }
            else {
              setTimeout(execute, 0)
            }
          }
          else {
            eventListener = execute
            document.addEventListener('DOMContentLoaded', eventListener)
          }
          break

        case 'network-idle':
          // 需要结合 requestIdleCallback 或其他网络检测机制
          if ('requestIdleCallback' in window) {
            (window as any).requestIdleCallback(execute)
          }
          else {
            setTimeout(execute, 1000)
          }
          break
      }
    }

    if (timeout) {
      timer = window.setTimeout(() => {
        cleanup()
        onTimeout?.()
      }, timeout)
    }

    handlePhase()

    // 返回取消函数
    return () => {
      isCancelled = true
      cleanup()
    }
  }

  /**
   * 等待 DOM 加载完成
   */
  static waitForDOMReady(): Promise<void> {
    return new Promise<void>((resolve) => {
      if (document.readyState !== 'loading') {
        resolve()
      }
      else {
        document.addEventListener('DOMContentLoaded', () => resolve(), { once: true })
      }
    })
  }

  /**
   * 等待所有资源加载完成
   */
  static onWindowLoad(
    callback: () => void,
    options?: AddEventListenerOptions,
  ): void {
    // 检查文档是否已经加载完成
    if (document.readyState === 'complete') {
      callback()
      return
    }

    // 如果还未加载完成，则添加事件监听
    window.addEventListener('load', callback, {
      once: true,
      ...options,
    })
  }

  /**
   * 等待 body 元素存在
   * @param timeout 超时时间（毫秒）
   */
  static waitForBodyElement(timeout: number = 10000): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      // 立即检查
      if (document.body) {
        return resolve()
      }

      // 设置超时
      const timer = setTimeout(() => {
        reject(new Error(`等待 body 元素超时（${timeout}ms）`))
      }, timeout)

      // 使用 MutationObserver 监听变化
      const observer = new MutationObserver((_mutations, obs) => {
        if (document.body) {
          clearTimeout(timer)
          obs.disconnect()
          resolve()
        }
      })

      // 监听 document 的子节点变化
      observer.observe(document.documentElement, {
        childList: true,
        subtree: true,
      })
    })
  }

  /**
   *  动态元素监听器
   */
  static setupDynamicHandler = (
    selector: string,
    callback: (el: HTMLElement) => void,
    options: {
      delay?: number // 防抖延迟时间(毫秒)
      root?: HTMLElement // 监听的根元素
    } = {},
  ): void => {
    const {
      delay = 300,
      root = document.body,
    } = options

    const debouncedHandler = FunctionUtils.throttle((mutation: MutationRecord) => {
      $(mutation.addedNodes).find(selector).each((_i, el) => callback(el))

      if ($(selector, root).length > 0)
        callback($(selector)[0])
    }, delay)

    const observer = new MutationObserver((mutations: MutationRecord[]) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          debouncedHandler(mutation)
        }
      })
    })

    observer.observe(root, {
      childList: true,
      subtree: true,
    })
  }
}
